
/**
 * Represents a XMPP error sub-packet. Typically, a server responds to a request that has
 * problems by sending the packet back and including an error packet. Each error has a code, type, 
 * error condition as well as as an optional text explanation. Typical errors are:<p>
 *
 * <table border=1>
 *      <hr><td><b>Code</b></td><td><b>XMPP Error</b></td><td><b>Type</b></td></hr>
 *      <tr><td>500</td><td>interna-server-error</td><td>WAIT</td></tr>
 *      <tr><td>403</td><td>forbidden</td><td>AUTH</td></tr>
 *      <tr><td>400</td<td>bad-request</td><td>MODIFY</td>></tr>
 *      <tr><td>404</td><td>item-not-found</td><td>CANCEL</td></tr>
 *      <tr><td>409</td><td>conflict</td><td>CANCEL</td></tr>
 *      <tr><td>501</td><td>feature-not-implemented</td><td>CANCEL</td></tr>
 *      <tr><td>302</td><td>gone</td><td>MODIFY</td></tr>
 *      <tr><td>400</td><td>jid-malformed</td><td>MODIFY</td></tr>
 *      <tr><td>406</td><td>no-acceptable</td><td> MODIFY</td></tr>
 *      <tr><td>405</td><td>not-allowed</td><td>CANCEL</td></tr>
 *      <tr><td>401</td><td>not-authorized</td><td>AUTH</td></tr>
 *      <tr><td>402</td><td>payment-required</td><td>AUTH</td></tr>
 *      <tr><td>404</td><td>recipient-unavailable</td><td>WAIT</td></tr>
 *      <tr><td>302</td><td>redirect</td><td>MODIFY</td></tr>
 *      <tr><td>407</td><td>registration-required</td><td>AUTH</td></tr>
 *      <tr><td>404</td><td>remote-server-not-found</td><td>CANCEL</td></tr>
 *      <tr><td>504</td><td>remote-server-timeout</td><td>WAIT</td></tr>
 *      <tr><td>502</td><td>remote-server-error</td><td>CANCEL</td></tr>
 *      <tr><td>500</td><td>resource-constraint</td><td>WAIT</td></tr>
 *      <tr><td>503</td><td>service-unavailable</td><td>CANCEL</td></tr>
 *      <tr><td>407</td><td>subscription-required</td><td>AUTH</td></tr>
 *      <tr><td>500</td><td>undefined-condition</td><td>WAIT</td></tr>
 *      <tr><td>400</td><td>unexpected-condition</td><td>WAIT</td></tr>
 *      <tr><td>408</td><td>request-timeout</td><td>CANCEL</td></tr>
 * </table>
 *
 * @author Matt Tucker
 */
class XMPPError {
public:
	typedef enum {
		UNDEFINED,
        WAIT,
        CANCEL,
        MODIFY,
        AUTH,
        CONTINUE
    } Type;
	   
    /**
     * A class to represent predefined error conditions.
     */
    class Condition {

	public:
		static Condition * interna_server_error;
        static Condition * forbidden;
        static Condition * bad_request;
        static Condition * conflict;
        static Condition * feature_not_implemented;
        static Condition * gone;
        static Condition * item_not_found;
        static Condition * jid_malformed;
        static Condition * no_acceptable;
        static Condition * not_allowed;
        static Condition * not_authorized;
        static Condition * payment_required;
        static Condition * recipient_unavailable;
        static Condition * redirect;
        static Condition * registration_required;
        static Condition * remote_server_error;
        static Condition * remote_server_not_found;
        static Condition * remote_server_timeout;
        static Condition * resource_constraint;
        static Condition * service_unavailable;
        static Condition * subscription_required;
        static Condition * undefined_condition;
        static Condition * unexpected_condition ;
        static Condition * request_timeout;

        Condition(std::string & value) {
            this->value = value;
        }

		std::string getValue() {
            return value;
        }
	private:
		std::string value;
    };
 
	/**
     * Creates a new error with the specified condition infering the type and code.
     * If the Condition is predefined, client code should be like:
     *     new XMPPError(XMPPError.Condition.remote_server_timeout);
     * If the Condition is not predefined, invocations should be like 
     *     new XMPPError(new XMPPError.Condition("my_own_error"));
     * 
     * @param condition the error condition.
     */
    XMPPError(Condition * condition);
	
    /**
     * Creates a new error with the specified condition and message infering the type and code.
     * If the Condition is predefined, client code should be like:
     *     new XMPPError(XMPPError.Condition.remote_server_timeout, "Error Explanation");
     * If the Condition is not predefined, invocations should be like 
     *     new XMPPError(new XMPPError.Condition("my_own_error"), "Error Explanation");
     *
     * @param condition the error condition.
     * @param messageText a message describing the error.
     */
    XMPPError(Condition * condition, std::string & messageText) ;

    /**
     * Creates a new  error with the specified code and no message.
     *
     * @param code the error code.
     * @deprecated new errors should be created using the constructor XMPPError(condition)
     */
    XMPPError(int code) ;

    /**
     * Creates a new error with the specified code and message.
     * deprecated
     *
     * @param code the error code.
     * @param message a message describing the error.
     * @deprecated new errors should be created using the constructor XMPPError(condition, message)
     */
    XMPPError(int code, std::string & message) ;

    /**
     * Creates a new error with the specified code, type, condition and message.
     * This constructor is used when the condition is not recognized automatically by XMPPError
     * i.e. there is not a defined instance of ErrorCondition or it does not applies the default 
     * specification.
     * 
     * @param code the error code.
     * @param type the error type.
     * @param condition the error condition.
     * @param message a message describing the error.
     */
    XMPPError(int code, Type type, std::string & condition, std::string & message,
            std::list<PacketExtension*> & extension) ;	
    /**
     * Returns the error condition.
     *
     * @return the error condition.
     */
	std::string getCondition() {
        return condition;
    }

    /**
     * Returns the error type.
     *
     * @return the error type.
     */
    Type getType() {
        return type;
    }

    /**
     * Returns the error code.
     *
     * @return the error code.
     */
    int getCode() {
        return code;
    } 
	
    /**
     * Returns the message describing the error, or null if there is no message.
     *
     * @return the message describing the error, or null if there is no message.
     */
	std::string getMessage() {
        return message;
    }

    /**
     * Returns the error as XML.
     *
     * @return the error as XML.
     */
	std::string toXML();
	
	std::string toString();
	
    /**
     * Returns an Iterator for the error extensions attached to the xmppError.
     * An application MAY provide application-specific error information by including a 
     * properly-namespaced child in the error element.
     *
     * @return an Iterator for the error extensions.
     */
	std::vector<PacketExtension*> getExtensions();

    /**
     * Returns the first patcket extension that matches the specified element name and
     * namespace, or <tt>null</tt> if it doesn't exist. 
     *
     * @param elementName the XML element name of the packet extension.
     * @param namespace the XML element namespace of the packet extension.
     * @return the extension, or <tt>null</tt> if it doesn't exist.
     */
    PacketExtension * getExtension(std::string & elementName, std::string & nameSpace);
	
    /**
     * Adds a packet extension to the error.
     *
     * @param extension a packet extension.
     */
    void addExtension(PacketExtension * extension);

    /**
     * Set the packet extension to the error.
     *
     * @param extension a packet extension.
     */
    void setExtension(std::vector<PacketExtension*> & extension);
	

private:
	
    /**
     * A class to represent the error specification used to infer common usage.
     */
    class ErrorSpecification {
	protected:
		static ErrorSpecification * specFor(Condition * condition);

        /**
         * Returns the error condition.
         *
         * @return the error condition.
         */
        Condition * getCondition() {
            return condition;
        }

        /**
         * Returns the error type.
         *
         * @return the error type.
         */
        Type getType() {
            return type;
        }

        /**
         * Returns the error code.
         *
         * @return the error code.
         */
        int getCode() {
            return code;
        }

	private:
        ErrorSpecification(Condition condition, Type type, int code) :
			code(code),
			type(type),
			condition(condition)
		{}

        static void errorSpecifications();
		
		int code;
        Type type;
        Condition * condition;
        static std::map<Condition*, ErrorSpecification*> instances;

	};

	void init(Condition*);

    int code;
    Type type;
	std::string condition;
	std::string message;
	std::list<PacketExtension*> applicationExtensions;

};
